{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "# Chatbot with message summarization & external DB memory\n",
        "\n",
        "## Review\n",
        "\n",
        "We've covered how to customize graph state schema and reducer.\n",
        "\n",
        "We've also shown a number of tricks for trimming or filtering messages in graph state.\n",
        "\n",
        "We've used these concepts in a Chatbot with memory that produces a running summary of the conversation.\n",
        "\n",
        "## Goals\n",
        "\n",
        "But, what if we want our Chatbot to have memory that persists indefinitely?\n",
        "\n",
        "Now, we'll introduce some more advanced checkpointers that support external databases.\n",
        "\n",
        "Here, we'll show how to use [Postgres as a checkpointer](https://langchain-ai.github.io/langgraph/how-tos/persistence_postgres/)"
      ],
      "metadata": {
        "id": "9iKKYdAkVoxu"
      }
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {
        "id": "Lwi6YkLu31aK"
      },
      "outputs": [],
      "source": [
        "%%capture --no-stderr\n",
        "%pip install -U langgraph langgraph-checkpoint-postgres psycopg psycopg-pool langchain_google_genai\n"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "from google.colab import userdata\n",
        "GEMINI_API_KEY = userdata.get('GEMINI_API_KEY')"
      ],
      "metadata": {
        "id": "i6Tn95wS4rxz"
      },
      "execution_count": 14,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "import os\n",
        "os.environ[\"LANGCHAIN_API_KEY\"] = userdata.get('LANGCHAIN_API_KEY')\n",
        "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
        "os.environ[\"LANGCHAIN_PROJECT\"] = \"langchain-academy\""
      ],
      "metadata": {
        "id": "qtL0cG1B8pMt"
      },
      "execution_count": 15,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Use sync connection¶\n",
        "This sets up a synchronous connection to the database.\n",
        "\n",
        "Synchronous connections execute operations in a blocking manner, meaning each operation waits for completion before moving to the next one. The DB_URI is the database connection URI, with the protocol used for connecting to a PostgreSQL database, authentication, and host where database is running. The connection_kwargs dictionary defines additional parameters for the database connection."
      ],
      "metadata": {
        "id": "AqAjciKmV6vl"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from google.colab import userdata\n",
        "DB_URI = userdata.get('DB_URI')"
      ],
      "metadata": {
        "id": "cP2TdPSB3-dO"
      },
      "execution_count": 16,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "from psycopg_pool import ConnectionPool\n",
        "from langgraph.checkpoint.postgres import PostgresSaver\n",
        "\n",
        "# Connection pool for efficient database access\n",
        "connection_kwargs = {\"autocommit\": True, \"prepare_threshold\": 0}\n",
        "\n",
        "# Create a persistent connection pool\n",
        "pool = ConnectionPool(conninfo=DB_URI, max_size=20, kwargs=connection_kwargs)\n",
        "\n",
        "# Initialize PostgresSaver checkpointer\n",
        "checkpointer = PostgresSaver(pool)\n",
        "checkpointer.setup()  # Ensure database tables are set up\n"
      ],
      "metadata": {
        "id": "0XCqHjWM4LTc"
      },
      "execution_count": 17,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Let's re-define our chatbot."
      ],
      "metadata": {
        "id": "w9All8mCV1o0"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from langchain_google_genai import ChatGoogleGenerativeAI\n",
        "from langchain_core.messages import SystemMessage, HumanMessage, RemoveMessage\n",
        "\n",
        "from langgraph.graph import END\n",
        "from langgraph.graph import MessagesState\n",
        "\n",
        "model: ChatGoogleGenerativeAI = ChatGoogleGenerativeAI(model = \"gemini-1.5-flash\", api_key =  GEMINI_API_KEY)\n",
        "\n",
        "class State(MessagesState):\n",
        "    summary: str\n",
        "\n",
        "# Define the logic to call the model\n",
        "def call_model(state: State) -> State:\n",
        "\n",
        "    # Get summary if it exists\n",
        "    summary = state.get(\"summary\", \"\")\n",
        "    print(f\"Using summary: {summary}\")\n",
        "\n",
        "    # If there is summary, then we add it\n",
        "    if summary:\n",
        "\n",
        "        # Add summary to system message\n",
        "        system_message = f\"Summary of conversation earlier: {summary}\"\n",
        "\n",
        "        # Append summary to any newer messages\n",
        "        messages = [SystemMessage(content=system_message)] + state[\"messages\"]\n",
        "\n",
        "    else:\n",
        "        messages = state[\"messages\"]\n",
        "\n",
        "    response = model.invoke(messages)\n",
        "    return {\"messages\": response}\n",
        "\n",
        "def summarize_conversation(state: State) -> State:\n",
        "    print(f\"Messages before summarizing: {len(state['messages'])}\")\n",
        "    # First, we get any existing summary\n",
        "    summary = state.get(\"summary\", \"\")\n",
        "    print(f\"Existing summary: {summary}\")\n",
        "\n",
        "    # Create our summarization prompt\n",
        "    if summary:\n",
        "\n",
        "        # A summary already exists\n",
        "        summary_message = (\n",
        "            f\"This is summary of the conversation to date: {summary}\\n\\n\"\n",
        "            \"Extend the summary by taking into account the new messages above:\"\n",
        "        )\n",
        "\n",
        "    else:\n",
        "        summary_message = \"Create a summary of the conversation above:\"\n",
        "\n",
        "\n",
        "    # Add prompt to our history\n",
        "    messages = state[\"messages\"] + [HumanMessage(content=summary_message)]\n",
        "    response = model.invoke(messages)\n",
        "    # Summarization logic\n",
        "    print(f\"New summary: {response.content}\")\n",
        "\n",
        "    # Delete all but the 2 most recent messages\n",
        "    delete_messages = [RemoveMessage(id=m.id) for m in state[\"messages\"][:-2]]\n",
        "\n",
        "    print(f\"Messages after truncation: {len(delete_messages)}\")\n",
        "    return {\"summary\": response.content, \"messages\": delete_messages}\n",
        "\n",
        "# Determine whether to end or summarize the conversation\n",
        "def should_continue(state: State) -> State:\n",
        "\n",
        "    \"\"\"Return the next node to execute.\"\"\"\n",
        "\n",
        "    messages = state[\"messages\"]\n",
        "    print(f\"Message count: {len(messages)}\")\n",
        "    # If there are more than six messages, then we summarize the conversation\n",
        "    if len(messages) > 6:\n",
        "        return \"summarize_conversation\"\n",
        "\n",
        "    # Otherwise we can just end\n",
        "    return END"
      ],
      "metadata": {
        "id": "kvU-4FnS4Wxu"
      },
      "execution_count": 18,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Now, we just re-compile with our postgres checkpointer."
      ],
      "metadata": {
        "id": "zzIVvGsXWap4"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from langgraph.graph import StateGraph, START, END\n",
        "from langgraph.graph.state import CompiledStateGraph\n",
        "\n",
        "# Redefine workflow\n",
        "workflow = StateGraph(State)\n",
        "workflow.add_node(\"conversation\", call_model)\n",
        "workflow.add_node(summarize_conversation)\n",
        "\n",
        "workflow.add_edge(START, \"conversation\")\n",
        "workflow.add_conditional_edges(\"conversation\", should_continue)\n",
        "workflow.add_edge(\"summarize_conversation\", END)\n",
        "\n",
        "# Compile the workflow with PostgreSQL checkpointer\n",
        "graph = workflow.compile(checkpointer=checkpointer)\n"
      ],
      "metadata": {
        "id": "d7wrnazV4mdo"
      },
      "execution_count": 19,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Now, we can invoke the graph several times."
      ],
      "metadata": {
        "id": "C_KTIXuvWkT0"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Configuration for thread\n",
        "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
        "\n",
        "# Start a conversation\n",
        "input_message = HumanMessage(content=\"hi! I'm Wania\")\n",
        "output = graph.invoke({\"messages\": [input_message]}, config)\n",
        "for m in output['messages'][-1:]:\n",
        "    m.pretty_print()\n",
        "\n",
        "# Check the persisted state\n",
        "graph_state = graph.get_state(config)\n",
        "graph_state"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Wk129iSWZapB",
        "outputId": "15ba3c91-a4a5-4866-dca9-9c3349540f60"
      },
      "execution_count": 20,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Using summary: \n",
            "Message count: 2\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "Hi Wania! It's nice to meet you.  How can I help you today?\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "StateSnapshot(values={'messages': [HumanMessage(content=\"hi! I'm Wania\", additional_kwargs={}, response_metadata={}, id='d76cb391-15b6-477b-a691-aebb30636879'), AIMessage(content=\"Hi Wania! It's nice to meet you.  How can I help you today?\\n\", additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []}, id='run-edfffea1-6d53-480d-b547-719e8d8e4d88-0', usage_metadata={'input_tokens': 8, 'output_tokens': 21, 'total_tokens': 29, 'input_token_details': {'cache_read': 0}})]}, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efa90f2-806a-658e-8001-e28bcb3c1e39'}}, metadata={'step': 1, 'source': 'loop', 'writes': {'conversation': {'messages': AIMessage(content=\"Hi Wania! It's nice to meet you.  How can I help you today?\\n\", additional_kwargs={}, response_metadata={'finish_reason': 'STOP', 'safety_ratings': [], 'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}}, id='run-edfffea1-6d53-480d-b547-719e8d8e4d88-0', usage_metadata={'input_tokens': 8, 'output_tokens': 21, 'total_tokens': 29, 'input_token_details': {'cache_read': 0}})}}, 'parents': {}, 'thread_id': '1'}, created_at='2024-11-22T20:20:03.290218+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efa90f2-7ace-66ef-8000-9a624abe57a3'}}, tasks=())"
            ]
          },
          "metadata": {},
          "execution_count": 20
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "# Configuration for thread\n",
        "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
        "\n",
        "# Start a conversation\n",
        "input_message = HumanMessage(content=\"I like painting pictures.\")\n",
        "output = graph.invoke({\"messages\": [input_message]}, config)\n",
        "for m in output['messages'][-1:]:\n",
        "    m.pretty_print()\n",
        "\n",
        "# Check the persisted state\n",
        "graph_state = graph.get_state(config)\n",
        "graph_state"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "HxWCVPkuZvf-",
        "outputId": "7df97126-f3c9-46d6-8e26-c33b05bba79b"
      },
      "execution_count": 21,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Using summary: \n",
            "Message count: 4\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "That's wonderful!  What kind of pictures do you like to paint?  Do you have a favorite medium (like oils, acrylics, watercolors)?\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "StateSnapshot(values={'messages': [HumanMessage(content=\"hi! I'm Wania\", additional_kwargs={}, response_metadata={}, id='d76cb391-15b6-477b-a691-aebb30636879'), AIMessage(content=\"Hi Wania! It's nice to meet you.  How can I help you today?\\n\", additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []}, id='run-edfffea1-6d53-480d-b547-719e8d8e4d88-0', usage_metadata={'input_tokens': 8, 'output_tokens': 21, 'total_tokens': 29, 'input_token_details': {'cache_read': 0}}), HumanMessage(content='I like painting pictures.', additional_kwargs={}, response_metadata={}, id='523173cf-f888-4141-9cde-66160074c905'), AIMessage(content=\"That's wonderful!  What kind of pictures do you like to paint?  Do you have a favorite medium (like oils, acrylics, watercolors)?\\n\", additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []}, id='run-d32dc7af-09a0-427c-a9e6-a06d79487552-0', usage_metadata={'input_tokens': 36, 'output_tokens': 34, 'total_tokens': 70, 'input_token_details': {'cache_read': 0}})]}, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efa90f2-b844-6011-8004-b7f28420f319'}}, metadata={'step': 4, 'source': 'loop', 'writes': {'conversation': {'messages': AIMessage(content=\"That's wonderful!  What kind of pictures do you like to paint?  Do you have a favorite medium (like oils, acrylics, watercolors)?\\n\", additional_kwargs={}, response_metadata={'finish_reason': 'STOP', 'safety_ratings': [], 'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}}, id='run-d32dc7af-09a0-427c-a9e6-a06d79487552-0', usage_metadata={'input_tokens': 36, 'output_tokens': 34, 'total_tokens': 70, 'input_token_details': {'cache_read': 0}})}}, 'parents': {}, 'thread_id': '1'}, created_at='2024-11-22T20:20:09.146534+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efa90f2-b27a-6779-8003-2a8720cf1fba'}}, tasks=())"
            ]
          },
          "metadata": {},
          "execution_count": 21
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "# Configuration for thread\n",
        "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
        "\n",
        "# Start a conversation\n",
        "input_message = HumanMessage(content=\"What's my name and what is my hobby?\")\n",
        "output = graph.invoke({\"messages\": [input_message]}, config)\n",
        "for m in output['messages'][-1:]:\n",
        "    m.pretty_print()\n",
        "\n",
        "# Check the persisted state\n",
        "graph_state = graph.get_state(config)\n",
        "graph_state"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "HxLLjBiBfZAL",
        "outputId": "b589d8ae-6302-41aa-bd0b-953dca9bfa69"
      },
      "execution_count": 22,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Using summary: \n",
            "Message count: 6\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "Your name is Wania, and your hobby is painting pictures.\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "StateSnapshot(values={'messages': [HumanMessage(content=\"hi! I'm Wania\", additional_kwargs={}, response_metadata={}, id='d76cb391-15b6-477b-a691-aebb30636879'), AIMessage(content=\"Hi Wania! It's nice to meet you.  How can I help you today?\\n\", additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []}, id='run-edfffea1-6d53-480d-b547-719e8d8e4d88-0', usage_metadata={'input_tokens': 8, 'output_tokens': 21, 'total_tokens': 29, 'input_token_details': {'cache_read': 0}}), HumanMessage(content='I like painting pictures.', additional_kwargs={}, response_metadata={}, id='523173cf-f888-4141-9cde-66160074c905'), AIMessage(content=\"That's wonderful!  What kind of pictures do you like to paint?  Do you have a favorite medium (like oils, acrylics, watercolors)?\\n\", additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []}, id='run-d32dc7af-09a0-427c-a9e6-a06d79487552-0', usage_metadata={'input_tokens': 36, 'output_tokens': 34, 'total_tokens': 70, 'input_token_details': {'cache_read': 0}}), HumanMessage(content=\"What's my name and what is my hobby?\", additional_kwargs={}, response_metadata={}, id='776f97d7-ab33-4746-9676-b3dfdfdaa4c9'), AIMessage(content='Your name is Wania, and your hobby is painting pictures.\\n', additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []}, id='run-02fd8b76-b4e6-4a71-be86-0e7af1190e26-0', usage_metadata={'input_tokens': 83, 'output_tokens': 14, 'total_tokens': 97, 'input_token_details': {'cache_read': 0}})]}, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efa90f3-3353-61b1-8007-621aaf4234ba'}}, metadata={'step': 7, 'source': 'loop', 'writes': {'conversation': {'messages': AIMessage(content='Your name is Wania, and your hobby is painting pictures.\\n', additional_kwargs={}, response_metadata={'finish_reason': 'STOP', 'safety_ratings': [], 'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}}, id='run-02fd8b76-b4e6-4a71-be86-0e7af1190e26-0', usage_metadata={'input_tokens': 83, 'output_tokens': 14, 'total_tokens': 97, 'input_token_details': {'cache_read': 0}})}}, 'parents': {}, 'thread_id': '1'}, created_at='2024-11-22T20:20:22.050221+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efa90f3-2fbc-6989-8006-756336db15f2'}}, tasks=())"
            ]
          },
          "metadata": {},
          "execution_count": 22
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "# Configuration for thread\n",
        "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
        "\n",
        "# Start a conversation\n",
        "input_message = HumanMessage(content=\"Can you describe about abstract paintings?\")\n",
        "output = graph.invoke({\"messages\": [input_message]}, config)\n",
        "for m in output['messages'][-1:]:\n",
        "    m.pretty_print()\n",
        "\n",
        "# Check the persisted state\n",
        "graph_state = graph.get_state(config)\n",
        "graph_state"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "iygDZ-_xhsQW",
        "outputId": "0a6afef6-7459-4f8e-8a96-01801d2db6be"
      },
      "execution_count": 23,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Using summary: \n",
            "Message count: 8\n",
            "Messages before summarizing: 8\n",
            "Existing summary: \n",
            "New summary: The conversation began with introductions, where I learned the user's name is Wania and her hobby is painting.  We then discussed abstract painting, with me providing a description of its characteristics, including its non-representational nature, emphasis on form and color, expressive qualities, stylistic variety, and subjective interpretation.\n",
            "\n",
            "Messages after truncation: 6\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "Abstract painting is a genre of art that doesn't attempt to represent an accurate depiction of visual reality but instead uses shapes, colors, forms, and gestural marks to achieve its effect.  It prioritizes expressing feelings, ideas, or concepts rather than depicting objects recognizably.\n",
            "\n",
            "Here are some key characteristics of abstract painting:\n",
            "\n",
            "* **Non-representational:**  Unlike realistic or figurative art, abstract art doesn't aim to portray a specific person, place, or thing in a recognizable way.\n",
            "\n",
            "* **Emphasis on form and color:** The focus shifts to the interplay of shapes, lines, colors, and textures.  These elements are used to create visual interest and evoke emotions.\n",
            "\n",
            "* **Expressiveness:** Abstract art often aims to convey emotions, moods, or ideas directly through the visual language of the painting.\n",
            "\n",
            "* **Variety of styles:** Abstract art encompasses a wide range of styles, including:\n",
            "    * **Geometric abstraction:** Uses geometric shapes and forms.\n",
            "    * **Lyrical abstraction:** Emphasizes fluidity and emotional expression.\n",
            "    * **Gestural abstraction:** Focuses on the physical act of painting and the artist's movement.\n",
            "    * **Color field painting:**  Uses large areas of flat color to create mood and atmosphere.\n",
            "\n",
            "* **Subjectivity:** The meaning and interpretation of an abstract painting are often subjective and open to the viewer's own experience and perspective. There's no single \"correct\" interpretation.\n",
            "\n",
            "\n",
            "Abstract art can be challenging to understand at first, but its beauty lies in its freedom from representational constraints and its ability to evoke powerful emotions and thoughts through pure visual expression.\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "StateSnapshot(values={'messages': [HumanMessage(content='Can you describe about abstract paintings?', additional_kwargs={}, response_metadata={}, id='87c170ad-ffa1-415f-b652-d2d7cd6d3b77'), AIMessage(content='Abstract painting is a genre of art that doesn\\'t attempt to represent an accurate depiction of visual reality but instead uses shapes, colors, forms, and gestural marks to achieve its effect.  It prioritizes expressing feelings, ideas, or concepts rather than depicting objects recognizably.\\n\\nHere are some key characteristics of abstract painting:\\n\\n* **Non-representational:**  Unlike realistic or figurative art, abstract art doesn\\'t aim to portray a specific person, place, or thing in a recognizable way.\\n\\n* **Emphasis on form and color:** The focus shifts to the interplay of shapes, lines, colors, and textures.  These elements are used to create visual interest and evoke emotions.\\n\\n* **Expressiveness:** Abstract art often aims to convey emotions, moods, or ideas directly through the visual language of the painting.\\n\\n* **Variety of styles:** Abstract art encompasses a wide range of styles, including:\\n    * **Geometric abstraction:** Uses geometric shapes and forms.\\n    * **Lyrical abstraction:** Emphasizes fluidity and emotional expression.\\n    * **Gestural abstraction:** Focuses on the physical act of painting and the artist\\'s movement.\\n    * **Color field painting:**  Uses large areas of flat color to create mood and atmosphere.\\n\\n* **Subjectivity:** The meaning and interpretation of an abstract painting are often subjective and open to the viewer\\'s own experience and perspective. There\\'s no single \"correct\" interpretation.\\n\\n\\nAbstract art can be challenging to understand at first, but its beauty lies in its freedom from representational constraints and its ability to evoke powerful emotions and thoughts through pure visual expression.\\n', additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []}, id='run-24c68d6c-3368-4a5f-8550-f5f9c19a095b-0', usage_metadata={'input_tokens': 106, 'output_tokens': 336, 'total_tokens': 442, 'input_token_details': {'cache_read': 0}})], 'summary': \"The conversation began with introductions, where I learned the user's name is Wania and her hobby is painting.  We then discussed abstract painting, with me providing a description of its characteristics, including its non-representational nature, emphasis on form and color, expressive qualities, stylistic variety, and subjective interpretation.\\n\"}, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efa90f3-b899-65b2-800b-746b642dd23e'}}, metadata={'step': 11, 'source': 'loop', 'writes': {'summarize_conversation': {'summary': \"The conversation began with introductions, where I learned the user's name is Wania and her hobby is painting.  We then discussed abstract painting, with me providing a description of its characteristics, including its non-representational nature, emphasis on form and color, expressive qualities, stylistic variety, and subjective interpretation.\\n\", 'messages': [RemoveMessage(content='', additional_kwargs={}, response_metadata={}, id='d76cb391-15b6-477b-a691-aebb30636879'), RemoveMessage(content='', additional_kwargs={}, response_metadata={}, id='run-edfffea1-6d53-480d-b547-719e8d8e4d88-0'), RemoveMessage(content='', additional_kwargs={}, response_metadata={}, id='523173cf-f888-4141-9cde-66160074c905'), RemoveMessage(content='', additional_kwargs={}, response_metadata={}, id='run-d32dc7af-09a0-427c-a9e6-a06d79487552-0'), RemoveMessage(content='', additional_kwargs={}, response_metadata={}, id='776f97d7-ab33-4746-9676-b3dfdfdaa4c9'), RemoveMessage(content='', additional_kwargs={}, response_metadata={}, id='run-02fd8b76-b4e6-4a71-be86-0e7af1190e26-0')]}}, 'parents': {}, 'thread_id': '1'}, created_at='2024-11-22T20:20:36.025050+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efa90f3-b1d4-68fa-800a-fbd02f0297f6'}}, tasks=())"
            ]
          },
          "metadata": {},
          "execution_count": 23
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [],
      "metadata": {
        "id": "gFqFzWDrWnt5"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Retrieve state using thread ID\n",
        "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
        "graph_state = graph.get_state(config)\n",
        "graph_state"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Cmwlf-TFYQqH",
        "outputId": "535362f8-27ba-438d-853f-252e58dbb4dc"
      },
      "execution_count": 24,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "StateSnapshot(values={'messages': [HumanMessage(content='Can you describe about abstract paintings?', additional_kwargs={}, response_metadata={}, id='87c170ad-ffa1-415f-b652-d2d7cd6d3b77'), AIMessage(content='Abstract painting is a genre of art that doesn\\'t attempt to represent an accurate depiction of visual reality but instead uses shapes, colors, forms, and gestural marks to achieve its effect.  It prioritizes expressing feelings, ideas, or concepts rather than depicting objects recognizably.\\n\\nHere are some key characteristics of abstract painting:\\n\\n* **Non-representational:**  Unlike realistic or figurative art, abstract art doesn\\'t aim to portray a specific person, place, or thing in a recognizable way.\\n\\n* **Emphasis on form and color:** The focus shifts to the interplay of shapes, lines, colors, and textures.  These elements are used to create visual interest and evoke emotions.\\n\\n* **Expressiveness:** Abstract art often aims to convey emotions, moods, or ideas directly through the visual language of the painting.\\n\\n* **Variety of styles:** Abstract art encompasses a wide range of styles, including:\\n    * **Geometric abstraction:** Uses geometric shapes and forms.\\n    * **Lyrical abstraction:** Emphasizes fluidity and emotional expression.\\n    * **Gestural abstraction:** Focuses on the physical act of painting and the artist\\'s movement.\\n    * **Color field painting:**  Uses large areas of flat color to create mood and atmosphere.\\n\\n* **Subjectivity:** The meaning and interpretation of an abstract painting are often subjective and open to the viewer\\'s own experience and perspective. There\\'s no single \"correct\" interpretation.\\n\\n\\nAbstract art can be challenging to understand at first, but its beauty lies in its freedom from representational constraints and its ability to evoke powerful emotions and thoughts through pure visual expression.\\n', additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': []}, id='run-24c68d6c-3368-4a5f-8550-f5f9c19a095b-0', usage_metadata={'input_tokens': 106, 'output_tokens': 336, 'total_tokens': 442, 'input_token_details': {'cache_read': 0}})], 'summary': \"The conversation began with introductions, where I learned the user's name is Wania and her hobby is painting.  We then discussed abstract painting, with me providing a description of its characteristics, including its non-representational nature, emphasis on form and color, expressive qualities, stylistic variety, and subjective interpretation.\\n\"}, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efa90f3-b899-65b2-800b-746b642dd23e'}}, metadata={'step': 11, 'source': 'loop', 'writes': {'summarize_conversation': {'summary': \"The conversation began with introductions, where I learned the user's name is Wania and her hobby is painting.  We then discussed abstract painting, with me providing a description of its characteristics, including its non-representational nature, emphasis on form and color, expressive qualities, stylistic variety, and subjective interpretation.\\n\", 'messages': [RemoveMessage(content='', additional_kwargs={}, response_metadata={}, id='d76cb391-15b6-477b-a691-aebb30636879'), RemoveMessage(content='', additional_kwargs={}, response_metadata={}, id='run-edfffea1-6d53-480d-b547-719e8d8e4d88-0'), RemoveMessage(content='', additional_kwargs={}, response_metadata={}, id='523173cf-f888-4141-9cde-66160074c905'), RemoveMessage(content='', additional_kwargs={}, response_metadata={}, id='run-d32dc7af-09a0-427c-a9e6-a06d79487552-0'), RemoveMessage(content='', additional_kwargs={}, response_metadata={}, id='776f97d7-ab33-4746-9676-b3dfdfdaa4c9'), RemoveMessage(content='', additional_kwargs={}, response_metadata={}, id='run-02fd8b76-b4e6-4a71-be86-0e7af1190e26-0')]}}, 'parents': {}, 'thread_id': '1'}, created_at='2024-11-22T20:20:36.025050+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1efa90f3-b1d4-68fa-800a-fbd02f0297f6'}}, tasks=())"
            ]
          },
          "metadata": {},
          "execution_count": 24
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "pool.close()"
      ],
      "metadata": {
        "id": "d_YbgTBs89_o"
      },
      "execution_count": 62,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Persisting state\n",
        "\n",
        "Using database like Postgres means state is persisted!\n",
        "\n",
        "For example, we can re-start the notebook kernel and see that we can still load from Postgres DB on disk.\n"
      ],
      "metadata": {
        "id": "Io-K7MCWinYF"
      }
    }
  ]
}